﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Collections.Generic;

namespace agTweener
{
    /// <summary>
    /// Tweener
    /// Controller for the Tweener class
    /// 
    /// author		Michael Cameron
    /// version		1.0.1
    /// Based on Flash Tweener Actionscript library by Zeh Fernando, Nate Chatellier
    /// Updated for Silverlight 2.0 Beta 1
    /// </summary>

    public delegate void TweenEvent(params object[] args);
    
    public class Tweener
    {
        private static List<TweenListItem> _tweenlist = new List<TweenListItem>();
        private static TimeSpan tickInterval;
        private static Storyboard _timer;

        public Tweener()
        {
        }

        public static int TweenCount
        {
            get { return _tweenlist.Count; }
        }

        public static Storyboard timer
        {
            get { return _timer; }
            set { _timer = value; }
        }
        
        /// <summary>
        /// Tween method for a FrameworkElement, simplest method with no event callbacks 
        /// </summary>
        /// <param name="target"></param>
        /// <param name="p"></param>
        /// <returns></returns>
        public static bool addTween(FrameworkElement target, TweenParameter p)
        {
            return addTween(target, p, null, null, null, null, null, null);
        }

        /// <summary>
        /// Tween method for a FrameworkElement - override with onComplete event with params 
        /// </summary>
        /// <param name="target"></param>
        /// <param name="p"></param>
        /// <param name="onComplete"></param>
        /// <param name="onCompleteParams"></param>
        /// <returns></returns>
        public static bool addTween(FrameworkElement target, TweenParameter p, TweenEvent onComplete, object[] onCompleteParams)
        {
            return addTween(target, p, null, null, null, null, onComplete, onCompleteParams);
        }

        /// <summary>
        /// Tween method for a FrameworkElement - override with onUpdate and onComplete events with params 
        /// </summary>
        /// <param name="target"></param>
        /// <param name="p"></param>
        /// <param name="onUpdate"></param>
        /// <param name="onUpdateParams"></param>
        /// <param name="onComplete"></param>
        /// <param name="onCompleteParams"></param>
        /// <returns></returns>
        public static bool addTween(FrameworkElement target, TweenParameter p, TweenEvent onUpdate, object[] onUpdateParams, TweenEvent onComplete, object[] onCompleteParams)
        {
            return addTween(target, p, null, null, onUpdate, onUpdateParams, onComplete, onCompleteParams);
        }

        /// <summary>
        /// Tween method for a FrameworkElement - override with onStart, onUpdate and onComplete events with params 
        /// </summary>
        /// <param name="target"></param>
        /// <param name="p"></param>
        /// <param name="onStart"></param>
        /// <param name="onStartParams"></param>
        /// <param name="onUpdate"></param>
        /// <param name="onUpdateParams"></param>
        /// <param name="onComplete"></param>
        /// <param name="onCompleteParams"></param>
        /// <returns></returns>
        public static bool addTween(FrameworkElement target, TweenParameter p, TweenEvent onStart, object[] onStartParams, TweenEvent onUpdate, object[] onUpdateParams, TweenEvent onComplete, object[] onCompleteParams)
        {
            TweenListItem t = new TweenListItem(target, p, onStart, onStartParams, onUpdate, onUpdateParams, onComplete, onCompleteParams);
            _tweenlist.Add(t);
            t.Completed += RemoveTween;
            if (_timer == null) InitTimer(target);
            return true;
        }

        /// <summary>
        /// Tween method for an array of doubles not associated with a FrameworkElement
        /// Updates to the values throughout the tween should be retrieved through the OnUpdate event
        /// Target values should be set in the TweenParameter.DoubleArray property
        /// </summary>
        /// <param name="doubleArray"></param>
        /// <param name="p"></param>
        /// <param name="onStart"></param>
        /// <param name="onStartParams"></param>
        /// <param name="onUpdate"></param>
        /// <param name="onUpdateParams"></param>
        /// <param name="onComplete"></param>
        /// <param name="onCompleteParams"></param>
        /// <returns></returns>
        public static bool addTween(double[] doubleArray, TweenParameter p, TweenEvent onStart, object[] onStartParams, TweenEvent onUpdate, object[] onUpdateParams, TweenEvent onComplete, object[] onCompleteParams)
        {
            TweenListItem t = new TweenListItem(doubleArray, p, onStart, onStartParams, onUpdate, onUpdateParams, onComplete, onCompleteParams);
            _tweenlist.Add(t);
            t.Completed += RemoveTween;
            if (p.TimerParent != null)
                if (_timer == null) InitTimer(p.TimerParent);
            return true;
        }

        /// <summary>
        /// Tween method for a point not associated with a FrameworkElement
        /// Updates to the values throughout the tween should be retrieved through the OnUpdate event
        /// Target values should be set in the TweenParameter.DoubleArray property
        /// </summary>
        /// <param name="point"></param>
        /// <param name="p"></param>
        /// <param name="onStart"></param>
        /// <param name="onStartParams"></param>
        /// <param name="onUpdate"></param>
        /// <param name="onUpdateParams"></param>
        /// <param name="onComplete"></param>
        /// <param name="onCompleteParams"></param>
        /// <returns></returns>
        public static bool addTween(Point point, TweenParameter p, TweenEvent onStart, object[] onStartParams, TweenEvent onUpdate, object[] onUpdateParams, TweenEvent onComplete, object[] onCompleteParams)
        {
            TweenListItem t = new TweenListItem(point, p, onStart, onStartParams, onUpdate, onUpdateParams, onComplete, onCompleteParams);
            _tweenlist.Add(t);
            t.Completed += RemoveTween;
            if (p.TimerParent != null)
                if (_timer == null) InitTimer(p.TimerParent);
            return true;
        }

        /// <summary>
        /// Removes a TweenListItem from the collection
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void RemoveTween(object sender, EventArgs e)
        {
            TweenListItem ti = (TweenListItem)sender;
            _tweenlist.Remove(ti);
            if (ti.OnComplete != null)
                ti.OnComplete(ti.OnCompleteParams);
        }

        /// <summary>
        /// Initialize the storyboard timer
        /// </summary>
        /// <param name="target"></param>
        private static void InitTimer(FrameworkElement target)
        {
            tickInterval = new TimeSpan(0, 0, 0, 0, 20);

            timer = new Storyboard();
            timer.Duration = new Duration(tickInterval);
            // If parent of target exists then add timer to this canvas otherwise
            // add the timer to the target canvas
            ((FrameworkElement)((FrameworkElement)target.Parent).Parent).Resources.Add("tweenStoryboard", timer);
            timer.Completed += new EventHandler(Tick);
            timer.Begin();
        }

        /// <summary>
        /// Handles the overall storyboard timer tick, calls HandleTick for each TweenListItem
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void Tick(Object sender, EventArgs e)
        {
            // Notify all items in the tweenlist of the tick event
            _tweenlist.ForEach(delegate(TweenListItem t)
            {
                t.HandleTick(tickInterval);
            });
            timer.Begin();
        }

    }
}